﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MyRoguelike.Tools
{
    static class Metrics
    {
        static Random _rnd = new Random();

        static public Random Random
        {
            get { return _rnd; }
        }

        /// <summary>
        /// Shows the position of a sprite
        /// </summary>
        /// <param name="window"></param>
        /// <param name="spriteBatch"></param>
        /// <param name="tex"></param>
        /// <param name="origin"></param>
        static public void PlacementSprite(GameWindow window, SpriteBatch spriteBatch, Texture2D tex, Vector2 origin)
        {
            // Get mouse position
            Vector2 mousePos = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

            // Show mouse pos
            window.Title = mousePos.ToString();

            // Draw texture
            spriteBatch.Draw(tex, mousePos, null, Color.White, 0f, origin, 1f, SpriteEffects.None, 0f);
        }
        static public void PlacementSprite(GameWindow window, SpriteBatch spriteBatch, Texture2D tex)
        {
            PlacementSprite(window, spriteBatch, tex, new Vector2(0, 0));
        }

        /// <summary>
        /// Calculate distance between two vectors
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        static public double GetDistance(Vector2 v1, Vector2 v2)
        {
            return Math.Sqrt(Math.Pow(v2.X - v1.X, 2) + Math.Pow(v2.Y - v1.Y, 2));
        }
        static public double GetDistance(Point p1, Point p2)
        {
            return GetDistance(
                new Vector2(p1.X, p1.Y),
                new Vector2(p2.X, p2.Y));
        }

        /// <summary>
        /// Smooth movement (decrease speed)
        /// </summary>
        /// <param name="currentPos"></param>
        /// <param name="finishPos"></param>
        /// <param name="speed"></param>
        /// <param name="startSmoothDistance"></param>
        /// <returns></returns>
        static public Vector2 SmoothMovement(Vector2 currentPos, Vector2 finishPos, GameTime gameTime, float speed, float startSmoothDistance)
        {
            if (currentPos != finishPos)
            {
                // Calc distances
                float currDist = (float)GetDistance(currentPos, finishPos);

                // Create move vector
                Vector2 moveVector = GetMoveVector(currentPos, finishPos);

                // Calc distance
                float curSpeed = speed;

                if (currDist < startSmoothDistance)
                    curSpeed = currDist / (startSmoothDistance / speed);

                // Return new position
                Vector2 newPos = currentPos + (moveVector * (curSpeed * (gameTime.ElapsedGameTime.Milliseconds / 16)));

                if (GetDistance(newPos, finishPos) > currDist)
                    newPos = finishPos;

                if (GetDistance(newPos, finishPos) < 0.5f)
                    newPos = finishPos;

                return newPos;
            }

            return finishPos;
        }

        /// <summary>
        /// Smooth movement (increase speed)
        /// </summary>
        /// <param name="startPos"></param>
        /// <param name="currentPos"></param>
        /// <param name="finishPos"></param>
        /// <param name="speed"></param>
        /// <param name="smoothDistance"></param>
        /// <returns></returns>
        static public Vector2 SmoothMovement(Vector2 startPos, Vector2 currentPos, Vector2 finishPos, GameTime gameTime, float speed, float smoothDistance)
        {
            if (currentPos != finishPos)
            {
                if (GetDistance(currentPos, finishPos) < (speed * (gameTime.ElapsedGameTime.Milliseconds / 16)))
                    return finishPos;

                // Create move vector
                Vector2 moveVector = new Vector2(0, 0);

                moveVector = (startPos == finishPos)
                                 ? new Vector2(1, 1)
                                 : GetMoveVector(startPos, finishPos);

                // Calc distance
                float curSpeed = speed;

                if (startPos == currentPos)
                    curSpeed = speed / 100f;
                else
                {
                    float curDist = (float)GetDistance(startPos, currentPos);
                    if (curDist < smoothDistance)
                        curSpeed = curDist / (smoothDistance / speed);
                }

                // Return new position
                Vector2 newPos = new Vector2(0, 0);
                newPos = currentPos + (moveVector * (curSpeed * (gameTime.ElapsedGameTime.Milliseconds / 16)));

                if (GetDistance(newPos, finishPos) < 0.5f)
                    newPos = finishPos;

                return newPos;
            }

            return finishPos;
        }

        /// <summary>
        /// Smooth increment
        /// </summary>
        /// <param name="start"></param>
        /// <param name="finish"></param>
        /// <param name="gameTime"></param>
        /// <param name="speed"></param>
        /// <param name="smoothDistance"></param>
        /// <returns></returns>
        static public float SmoothIncrement(float start, float finish, GameTime gameTime, float speed, float smoothDistance, float rounding = 0f)
        {
            if (start != finish)
            {
                float result = 0;

                // Get current distance
                float currDist = Math.Abs(finish - start);

                if (currDist <= rounding) return finish;

                if (currDist > smoothDistance)
                    result = start + (speed * (gameTime.ElapsedGameTime.Milliseconds / 16));
                else
                    result = start + (((speed / smoothDistance) * currDist) * (gameTime.ElapsedGameTime.Milliseconds / 16));

                if (((finish > 0) && (result > finish))
                    || ((finish < 0) && (result < finish))) result = finish;

                return result;
            }

            return finish;
        }

        /// <summary>
        /// Returns move vector
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        static public Vector2 GetMoveVector(Vector2 v1, Vector2 v2)
        {
            if (v1 != v2)
            {
                // Create move vector
                Vector2 moveVector = new Vector2(0, 0);

                // Get X and Y distance
                float distX = Math.Abs(v1.X - v2.X);
                float distY = Math.Abs(v1.Y - v2.Y);

                // Calc move vector
                if (distX > distY)
                {
                    moveVector.X = 1;
                    moveVector.Y = distY / distX;
                }
                else if (distY > distX)
                {
                    moveVector.X = distX / distY;
                    moveVector.Y = 1;
                }
                else if (distX == distY)
                {
                    moveVector.X = 1;
                    moveVector.Y = 1;
                }

                // Set move vector
                if (v1.X > v2.X) moveVector.X = -moveVector.X;
                if (v1.Y > v2.Y) moveVector.Y = -moveVector.Y;

                return moveVector;
            }
            
            return new Vector2(0, 0);
        }

        /// <summary>
        /// Returns random move vector
        /// </summary>
        /// <returns></returns>
        static public Vector2 GetRandomMoveVector(bool noZero = false)
        {
            Vector2 newMove = new Vector2(0f, 0f);

            // Set vector
            if (noZero)
                while ((newMove.X == 0f) && (newMove.Y == 0f))
                {
                    newMove.X = (float)_rnd.Next(10) / 10f;
                    newMove.Y = (float)_rnd.Next(10) / 10f;
                }
            else
            {
                newMove.X = (float)_rnd.Next(10) / 10f;
                newMove.Y = (float)_rnd.Next(10) / 10f;
            }

            if (newMove.X != newMove.Y)
            {
                if (newMove.X > newMove.Y)
                {
                    float p = 1f / (newMove.X / 100f);
                    newMove.X = 1f;
                    newMove.Y = (newMove.Y / 100f) * p;
                }
                else
                {
                    float p = 1f / (newMove.Y / 100f);
                    newMove.Y = 1f;
                    newMove.X = (newMove.X / 100f) * p;
                }
            }
            else newMove.X = newMove.Y = 1f;

            // Set directions
            switch (_rnd.Next(2))
            {
                case 0:
                    newMove.X = -newMove.X;
                    break;
            }

            switch (_rnd.Next(2))
            {
                case 0:
                    newMove.Y = -newMove.Y;
                    break;
            }

            return newMove;
        }

        /// <summary>
        /// Get near multiple value
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <returns></returns>
        static public int NearMultiple(int value, int min)
        {
            int result = min;

            while (true)
            {
                if (((int)(value / result) * result) == value)
                    return result;

                result += 1;
            }
        }
    }
}
